/*  
 *  Copyright 2006 Paul Jack.
 *
 *  This file is part of the Dex compiler suite. 
 *  
 *  Dex is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 2 of the License, or (at your
 *  option) any later version.
 *  
 *  Dex is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 *  Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */
package dex.compiler.model.statement;


import dex.compiler.model.expression.Expression;
import dex.compiler.model.expression.ExpressionWalker;


/**
 * Walks a tree of statement nodes.
 */
public abstract class StatementWalker extends ExpressionWalker implements StatementVisitor {


	/**
	 * Constructs a new <code>StatementWalker</code>.
	 */
	public StatementWalker() {
		super();
	}


	/**
	 * Walks the given statement.  One of the protected <code>walk</code>
	 * methods will be invoked based on the specific type of the given
	 * statement.
	 * 
	 * @param statement  the statement to walk
	 */
	public void walk(Statement statement) {
		if (statement instanceof Assignment) {
			walkAssignment((Assignment)statement);
			return;
		}
		if (statement instanceof Block) {
			walkBlock((Block)statement);
			return;
		}
		if (statement instanceof CallStatement) {
			walkCallStatement((CallStatement)statement);
			return;
		}
		if (statement instanceof Comment) {
			walkComment((Comment)statement);
			return;
		}
		if (statement instanceof Declaration) {
			walkDeclaration((Declaration)statement);
			return;
		}
		if (statement instanceof DecrementStatement) {
			walkDecrementStatement((DecrementStatement)statement);
			return;
		}
		if (statement instanceof ForStatement) {
			walkForStatement((ForStatement)statement);
			return;
		}
		if (statement instanceof IfStatement) {
			walkIfStatement((IfStatement)statement);
			return;
		}
		if (statement instanceof IncrementStatement) {
			walkIncrementStatement((IncrementStatement)statement);
			return;
		}
		if (statement instanceof ReturnStatement) {
			walkReturnStatement((ReturnStatement)statement);
			return;
		}
		if (statement instanceof SwitchStatement) {
			walkSwitchStatement((SwitchStatement)statement);
			return;
		}
		if (statement instanceof ThrowStatement) {
			walkThrowStatement((ThrowStatement)statement);
			return;
		}
		if (statement instanceof TryStatement) {
			walkTryStatement((TryStatement)statement);
			return;
		}
		if (statement instanceof WhileStatement) {
			walkWhileStatement((WhileStatement)statement);
			return;
		}
		throw new AssertionError(statement.getClass().getName());
	}


	/**
	 * Walks the given <code>Assignment</code>.  The default implementation
	 * first walks the lvalue expression, the walks the rvalue expression,
	 * and finally visits the given statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkAssignment(Assignment statement) {
		walk(statement.getLValue());
		walk(statement.getRValue());
		this.visitAssignment(statement);
	}


	/**
	 * Walks the given <code>Block</code>.  The default implementation
	 * first enters the given block, then walks each statement from
	 * first to last, and finally exits the block.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkBlock(Block statement) {
		this.enterBlock(statement);
		for (Statement s: statement.getStatements()) {
			walk(s);
		}
		this.exitBlock(statement);
	}


	/**
	 * Walks the given <code>CallStatement</code>.  The default implementation
	 * walks the internal call expression, then visits the given statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkCallStatement(CallStatement statement) {
		walk(statement.getCallExpression());
		this.visitCallStatement(statement);
	}


	/**
	 * Walks the given <code>Comment</code>.  The default implementation just
	 * visits the given statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkComment(Comment statement) {
		this.visitComment(statement);
	}


	/**
	 * Walks the given <code>Declaration</code>.  The default implementation
	 * first walks the initializer expression (if present), then visits
	 * the given statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkDeclaration(Declaration statement) {
		if (statement.getInitializer() != null) {
			walk(statement.getInitializer());
		}
		this.visitDeclaration(statement);
	}


	/**
	 * Walks the given <code>DecrementStatement</code>.  The default implementation
	 * first walks the lvalue expression, then visits the given statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkDecrementStatement(DecrementStatement statement) {
		walk(statement.getLValue());
		this.visitDecrementStatement(statement);
	}


	/**
	 * Walks the given <code>ForStatement</code>.  The default implementation
	 * first enters the given for statement, then walks the initializer
	 * statement (if present), then walks the test expression, then walks
	 * the modifier statement (if present), and finally exits the for statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkForStatement(ForStatement statement) {
		this.enterForStatement(statement);
		if (statement.getInitializer() != null) {
			walk(statement.getInitializer());
		}
		walk(statement.getTest());
		if (statement.getModifier() != null) {
			walk(statement.getModifier());
		}
		walk(statement.getBody());
		this.exitForStatement(statement);
	}


	/**
	 * Walks the given <code>IfStatement</code>.  The default implementation
	 * first walks the test expression, then walks the positive branch 
	 * statement, then walks the negative branch statement (if present),
	 * and finally visits the given statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkIfStatement(IfStatement statement) {
		walk(statement.getTest());
		walk(statement.getPositiveBranch());
		if (statement.getNegativeBranch() != null) {
			walk(statement.getNegativeBranch());
		}
		this.visitIfStatement(statement);
	}


	/**
	 * Walks the given <code>IncrementStatement</code>.  The default implementation
	 * first walks the lvalue expression, then visits the given statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkIncrementStatement(IncrementStatement statement) {
		walk(statement.getLValue());
		this.visitIncrementStatement(statement);
	}


	/**
	 * Walks the given <code>ReturnStatement</code>.  The default implementation
	 * first walks the return expression (if present), then visits the given
	 * statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkReturnStatement(ReturnStatement statement) {
		if (statement.getExpression() != null) {
			walk(statement.getExpression());
		}
		this.visitReturnStatement(statement);
	}


	/**
	 * Walks the given <code>SwitchStatement</code>.
	 *
	 * FIXME: The default implementation is wrong.  First, SwitchStatement needs to 
	 * become a container that allows comments.  Next, the default clause of a switch
	 * statement needs to occur in the right place.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkSwitchStatement(SwitchStatement statement) {
		walk(statement.getTest());
		for (SwitchCase sc: statement.getCases()) {
			for (Expression e: sc.getTests()) {
				walk(e);
			}
			walk(sc.getBody());
			this.visitSwitchCase(sc);
		}
		if (statement.getDefaultStatement() != null) {
			walk(statement.getDefaultStatement());
		}
		this.visitSwitchStatement(statement);
	}


	/**
	 * Walks the given <code>ThrowStatement</code>.  The default section first
	 * walks the subexpression, then visits the given statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkThrowStatement(ThrowStatement statement) {
		walk(statement.getExpression());
		this.visitThrowStatement(statement);
	}


	/**
	 * Walks the given <code>TryStatement</code>.  The default section first
	 * walks the try section, then walks each catch block from first to last,
	 * then walks the finally block (if present), and finally visits the
	 * given statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkTryStatement(TryStatement statement) {
		walk(statement.getTrySection());
		for (CatchBlock cb: statement.getCatchSection()) {
			walk(cb.getBlock());
			this.visitCatchBlock(cb);
		}
		if (statement.getFinallySection() != null) {
			walk(statement.getFinallySection());
		}
		this.visitTryStatement(statement);
	}


	/**
	 * Walks the given <code>WhileStatement</code>.  The default implementation
	 * first walks the test expression, the walks the body, then finally
	 * visits the given statement.
	 *
	 * @param statement  the statement to walk
	 */
	protected void walkWhileStatement(WhileStatement statement) {
		walk(statement.getTest());
		walk(statement.getBody());
		this.visitWhileStatement(statement);
	}



}
